package com.gw2.gw2world.controller;

import java.io.BufferedReader;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Vector;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import org.json.JSONObject;

import com.gw2.gw2world.model.Event;

import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.AsyncTask;
//import android.util.JsonReader;
import android.view.View;

/** Manage download of pages
 */
public class EventDownloader extends AsyncTask<Void, Void, Void> {
	public static final int NO_MAP=0;
	public static final int BIT_GROUP=0x0100;
	@SuppressWarnings("unused")
	private static final String TAG = "EventDownloader";
	private List<DownloadThread> downloadTask=new ArrayList<DownloadThread>(3);
	private List<Future<String> > json=new ArrayList<Future<String> >(3);
	private ExecutorService executor;
	private View workingView=null;
	private boolean guesting=true, remove=false, all=false, onlyMeta=false, showGroupEvents=true, showLivingStoryEvents=true;
	private int min_level=1, max_level=80;
	private OnTaskCompleted listener=null;
	private Vector<Event> events;
	private Map<String,Event> event_info;
	private int map_id=0, server_id=0;
	private DatabaseOpenHelper dbHelper;
	private SQLiteDatabase db;
	private String language;
	private int[] servers=new int[] {0,0,0};
	private Cursor c;
	//private String serverFilter="world_id\":1";
	public EventDownloader(Context ct, String lang, int home, int guest1, int guest2, boolean onlyMetaEvent) {
		language=lang;
		onlyMeta=onlyMetaEvent;
		servers[0]=home;
		servers[1]=guest1;
		servers[2]=guest2;
		dbHelper = new DatabaseOpenHelper(ct, "database", null, 1);
		//if(prefs.getInt("home_server", 0)>1999) serverFilter="world_id\":2";
		db = dbHelper.getReadableDatabase();
	}
	/** Filter event by map before downloading
	 * @param id Map ID
	 */
	public void setMap(int id) {map_id=id;}
	public void setServer(int id) {server_id=id;}
	public void setRangeLevel(int min, int max) {
		min_level=min;
		max_level=max;
	}
	public void setShowGroupEvents(boolean showGroupEvents) {this.showGroupEvents = showGroupEvents;}
	public void setShowLivingStoryEvents(boolean showLivingStoryEvents) {this.showLivingStoryEvents = showLivingStoryEvents;}
	public Vector<Event> getEvents() {return events;}
	public List<Event> getServerEvents(int server_id) {
		List<Event> result=new ArrayList<Event>();
		for(Event e : events)
			if(e.server_id==server_id)
				result.add(e);
		return result;
	}
	/** Enable/disable data download from guesting servers
	 * @param enabled Default is true
	 */
	public void setGuesting(boolean enabled) {guesting=enabled;}
	public void setAllServers(boolean enabled) {all=enabled;}
	/** Keep (or not) inactive events in the result
	 * @param v false if you want to keep them
	 */
	public void removeInactiveEvents(boolean v) {remove=v;}
	/** Set a UI element when it is working
	 * @param v UI element
	 */
	public void setWorkingView(View v) { workingView=v; }
	
	private String getSqlEvent() {
		StringBuilder sb=new StringBuilder("SELECT * FROM event_name n INNER JOIN event e ON e.id=n.id WHERE level>=").append(min_level).append(" AND level<=").append(max_level);
		sb.append(" AND lang=?");
		if(onlyMeta) sb.append(" AND master IS NOT NULL");
		if(map_id>0) sb.append(" AND location=").append(map_id);
		if(!showGroupEvents) sb.append(" AND (flag&1)=0");
		if(!showLivingStoryEvents) sb.append(" AND (flag&32)=0");
		return sb.toString();
	}
	private void getEventInfo() {
		c=db.rawQuery(getSqlEvent(), new String[]{language});
		//event_level_and_flag=new HashMap<String, Integer>(c.getCount());
		event_info=new HashMap<String, Event>(c.getCount());
		Event e;
		while (c.moveToNext()) {
			e=new Event();
			e.id=c.getString(0);
			e.name=c.getString(2);
			e.level=c.getInt(5);
			e.flag=c.getInt(7);
			e.group=(e.flag&1)==1;
			e.depth=c.getInt(4);
			e.living_world=(e.flag&32)==32;
			if(!c.isNull(11)) e.master=c.getInt(11);
			event_info.put(c.getString(0), e);
			//android.util.Log.d(TAG, e.toString()+ " flag: "+e.flag); 
		}
	}
	
	@Override
	protected void onPreExecute() {
		if(event_info ==null) getEventInfo();
		events=new Vector<Event>(event_info.size()*(all?52:3));
		c.close();
		db.close();
		dbHelper.close();
		if(workingView!=null) workingView.setVisibility(View.VISIBLE);
		json.clear();
		super.onPreExecute();
	}
	@Override
	protected Void doInBackground(Void... params) {
		//download
		downloadTask.clear();
		executor=Executors.newFixedThreadPool(3);
		if(all) {
			downloadTask.add(new DownloadThread(server_id, map_id));
		} else {
			if (servers[0]==0 || !guesting)
				downloadTask.add(new DownloadThread(servers[0], map_id));
			else if (servers[2]==0) {
				downloadTask.add(new DownloadThread(servers[0], map_id));
				downloadTask.add(new DownloadThread(servers[1], map_id));
			}
			else {
				downloadTask.add(new DownloadThread(servers[0], map_id));
				downloadTask.add(new DownloadThread(servers[1], map_id));
				downloadTask.add(new DownloadThread(servers[2], map_id));
			}
		}
		for(int i=0; i<downloadTask.size(); i++) json.add(executor.submit(downloadTask.get(i)));
		executor.shutdown();
		boolean region_eu=servers[0]>1999;
		while(!executor.isTerminated());
		//process result
		Event info;
		try {
			for (int i = 0; i < json.size(); i++) {
				if (json.get(i).get().equals("")) continue;
				Event e;
				StringReader sr=new StringReader(json.get(i).get());
				String line;
				BufferedReader br=new BufferedReader(sr);
				//android.util.Log.d("EventDownloader", "start event filtering");
				while ((line = br.readLine()) != null) {
					//if(!line.contains(serverFilter)) continue;
					if(line.contains("{\"events\":[")) line=line.substring(11);
					if(line.contains("}]}")) line=line.substring(0, line.length()-2);
					else line=line.substring(0,line.length()-1);
					//android.util.Log.d("EventDownloader", line);
					//for(String k : event_names.keySet())
					for(String k : event_info.keySet())
						if(line.contains(k)) {
							JSONObject o=new JSONObject(line);
							e=new Event();
							e.id=k;
							e.server_id=o.getInt("world_id");
							if(region_eu && e.server_id<2000) continue;
							if(!region_eu && e.server_id>1999) continue;
							e.state=o.getString("state");
							e.map_id=o.getInt("map_id");
							info=event_info.get(e.id);
							if((!showGroupEvents && info.group) || (!showLivingStoryEvents && info.living_world)) continue;
							if(e.isActive() || !remove) {
								e.name=info.name;
								if(e.name==null) continue;
								e.master=info.master;
								e.flag=info.flag;
								e.depth=info.depth;
								e.living_world=info.living_world;
								e.level=info.level;
								e.group=info.group;
								events.add(e);
							}
						}
				}
				br.close();
				sr.close();
			}
			Collections.sort(events, Event.SORT_BY_LEVEL);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}
	@Override
	protected void onPostExecute(Void result) {
		if(workingView!=null) workingView.setVisibility(View.GONE);
		if(listener!=null) listener.onTaskCompleted();
		super.onPostExecute(result);
	}
	/** Set a listener for completed job
	 * @param l Listener
	 */
	public void setOnTaskCompletedListener(OnTaskCompleted l) {listener=l;}
	public Event getEventFromId(String id) {
		Event e=new Event();
		e.name=event_info.get(id).name;
		if(e.name==null) return null;
		e.level=event_info.get(id).level;
		e.group=event_info.get(id).group;
		//get the map
		c=db.query("event", null, "id=?", new String[]{id}, null, null, null,"1");
		if(c.moveToFirst())
			e.map_id=c.getInt(3);
		else e=null;
		c.close();
		return e;
	}
}
